<!-- WEB 仓库 》 表单 -->

<template>
  <el-form ref="form" :class="formClass" v-bind="config">
    <!-- 展示标题 -->
    <div v-if="title" :class="`${className}-title`">{{ title }}</div>

    <!-- 展示插槽 -->
    <slot />
  </el-form>
</template>

<script>
// ============================== 导入依赖 ============================== //

import CORE from '@/CORE'
import mixPath from '../mixPath'
import mixForm from '../form/mixForm'

function addHash(errs) {
  location.hash = `form-item-${Object.keys(errs)[0]}`
}

function clearHash() {
  if (history) history.replaceState(null, '', `${location.pathname}${location.search}`)
  else location.hash = ''
}

const name = 'WebForm'

// ============================== 导出组件 ============================== //

export default {
  /**
   * 名称定义 (实际使用名称)
   */
  name,

  /**
   * 混入列表 (配置选项合并)
   */
  mixins: [mixPath],

  /**
   * 属性注册 (抽取以便查阅)
   */
  props: CORE.WEB[name],

  /**
   * 状态数据 (函数返回对象)
   */
  data() {
    return {
      reseting: false, // 正在重置中
    }
  },

  /**
   * 计算属性 (慎用箭头函数)
   */
  computed: {
    /**
     * 尺寸
     */
    size: mixForm.computed.size,

    /**
     * 标签文字个数
     */
    wordNum() {
      const from2to10 = Array(9).fill().map((_, i) => i + 2)
      for (const num of from2to10) {
        if (this[`word${num}`]) return num
      }
      return this.words
    },

    /**
     * 最终的标签宽度
     */
    myLabelWidth() {
      const { getSize, wordSize, blankWidth, widthUnit, labelWidth, wordNum } = this
      if (wordNum) getSize(`${wordNum * wordSize + blankWidth}${widthUnit}`)
      return getSize(labelWidth)
    },

    /**
     * 表单配置
     */
    config() {
      const { elProps, state, one, rules, labelPosition, myLabelWidth, colon, disabled, noCache, size } = this
      const isShow = state.modal === 'show'
      return {
        ...elProps,
        model: one || state.one,
        disabled: disabled || isShow,
        rules,
        labelPosition,
        labelWidth: myLabelWidth,
        labelSuffix: colon,
        hideRequiredAsterisk: elProps.hideRequiredAsterisk || isShow,
        autocomplete: noCache ? 'off' : 'on',
        size,
      }
    },

    /**
     * 表单样式类
     */
    formClass() {
      const { className, reseting, size } = this
      const resetStr = reseting ? ` ${className}-reseting` : ''
      const sizeStr = size ? ` CoreWeb-Form-${size}` : ''
      return `${className}${resetStr}${sizeStr}`
    },

    /**
     * 是否为历史模式
     */
    isHistoryMode: () => CORE.ROUTE.router.mode === 'history',
  },

  /**
   * 监听属性 (深度 deep 首次 immediate)
   */
  watch: {
    /**
     * 监听弹窗可见
     */
    'state.visible'() {
      const { state, verifyReset } = this
      if (['show', 'add', 'edit'].includes(state.modal)) verifyReset()
    },
  },

  /**
   * 挂载完成 (先抽取再调用)
   */
  mounted() {
    const refForm = this.$refs.form
    refForm.check = this.check

    // 表单组件保存到 state 中
    if (this.state.refForm !== undefined) this.commit('setRefForm', refForm)

    // 重置校验
    this.verifyReset()
  },

  /**
   * 本地方法 (轻量重要在前)
   */
  methods: {
    /**
     * 重置校验 (下次弹窗提交，可重新校验全部字段)
     */
    async verifyReset() {
      if (this.isHistoryMode) clearHash()
      if (!this.$refs.form) return

      setTimeout(() => this.$refs.form && this.$refs.form.clearValidate())
      this.reseting = true
      setTimeout(() => (this.reseting = false), 300)
    },

    /**
     * 异步检查 (重复提交时，可以闪烁提示)
     */
    async check(prop) {
      clearHash()
      this.$refs.form.clearValidate(prop)
      await this.$nextTick()

      return new Promise((resolve) => {
        if (prop) { // 校验单字段
          this.$refs.form.validateField(prop, (errMsg, errs) => {
            if (this.isHistoryMode) !errMsg ? clearHash() : addHash(errs)

            if (!errMsg) resolve()
            else this.$emit('error', errs)
          })
        } else { // 校验全表单
          this.$refs.form.validate((valid, errs) => {
            if (this.isHistoryMode) valid ? clearHash() : addHash(errs)

            if (valid) resolve()
            else this.$emit('error', errs)
          })
        }
      })
    },
  },
}
</script>
